#if !defined(AFX_LISTPANELVIEW_H__20071124_9610_2C38_D15D_0080AD509054__INCLUDED_)
#define AFX_LISTPANELVIEW_H__20071124_9610_2C38_D15D_0080AD509054__INCLUDED_

#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000

#include "TableOfContentsView.h"
#include "ImageListView.h"


class CListPanelView : 
   public ATL::CWindowImpl<CListPanelView>,
   public CVistaBufferedPaint
{
public:
   DECLARE_WND_CLASS_EX(_T("WTL_PhotoListPanel"), CS_PARENTDC, COLOR_WINDOW)

   enum { FADESTOP_TIMERID = 88 };
   enum { DEF_STATE = ID_VIEW_THUMBNAILS | (1<<31) };

   CTabelOfContentsView m_wndContents;
   CPhotoImageListView m_wndList;

   UINT m_uCurState;          // Current BufferedAnimation state
   UINT m_uNewState;          // New BufferedAnimation state
   CBitmap m_bmpCompat;       // Bitmap containing capture of window view prior to animation
   BOOL m_bShowTOC;           // Show TableOfContents view?

   CListPanelView() : m_bShowTOC(TRUE), m_uCurState((UINT) DEF_STATE), m_uNewState((UINT) DEF_STATE)
   {
   }

   BEGIN_MSG_MAP(CListPanelView)
      MESSAGE_HANDLER(WM_CREATE, OnCreate)
      MESSAGE_HANDLER(WM_SIZE, OnSize)
      MESSAGE_HANDLER(WM_TIMER, OnTimer)
      MESSAGE_HANDLER(WM_PAINT, OnPaint)
      MESSAGE_HANDLER(WM_ERASEBKGND, OnEraseBkgnd)
      MESSAGE_HANDLER(WM_USER_TOC_CHANGE, OnViewTocChange)
      MESSAGE_HANDLER(WM_USER_VIEWMODE_CHANGE, OnViewModeChange)
      REFLECT_NOTIFICATIONS()
   END_MSG_MAP()

   LRESULT OnCreate(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
   {
      m_wndContents.Create(m_hWnd, rcDefault, NULL, WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN, 0, IDC_INFO);
      m_wndList.Create(m_hWnd, rcDefault, NULL, WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | LVS_ICON | LVS_ALIGNTOP | LVS_AUTOARRANGE | LVS_SHOWSELALWAYS, 0, IDC_INFO);     
      m_wndList.SetFocus();
      return 0;
   }

   LRESULT OnEraseBkgnd(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
   {
      return 1;  // Clients fill the entire area
   }

   LRESULT OnViewModeChange(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& /*bHandled*/)
   {
      if( _CaptureView() ) {
         m_uNewState = (wParam & 0x7FFFFFFF) | (m_bShowTOC << 31);
         Invalidate();
      }
      return 0;
   }

   LRESULT OnViewTocChange(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
   {
      m_bShowTOC = !m_bShowTOC;
      UINT uStyle = 0;
      if( _CaptureView() ) {
         m_uNewState = (m_uCurState & 0x7FFFFFFF) | (m_bShowTOC << 31);
         uStyle = SWP_NOREDRAW; // FIX: Prevent annoying flicker
         Invalidate();
      }
      // We can continue to rearrange the windows.
      CRect rcClient;
      GetClientRect(&rcClient);
      _UpdateLayout(rcClient, SWP_SHOWWINDOW | uStyle);
      return 0;
   }

   LRESULT OnTimer(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
   {
      if( wParam == FADESTOP_TIMERID ) 
      {
         KillTimer(FADESTOP_TIMERID);
         if( !m_bmpCompat.IsNull() ) m_bmpCompat.DeleteObject();
         // Show child windows again according to settings...
         CRect rcClient;
         GetClientRect(&rcClient);
         _UpdateLayout(rcClient, SWP_SHOWWINDOW);
      }
      bHandled = FALSE;
      return 0;
   }

   LRESULT OnPaint(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)
   {
      CPaintDC dc(m_hWnd);

      enum { ANIMATION_DURATION = 300 };

      LRESULT lRes = 0;

      if( IsCompositionActive() && !m_bmpCompat.IsNull() ) 
      {
         // See if this paint was generated by a soft-fade animation
         if( !BufferedPaintRenderAnimation(m_hWnd, dc) ) {
            // Check if animation is needed. If not set dwDuration to 0.
            BP_ANIMATIONPARAMS ap = { 0 };
            ap.cbSize = sizeof(BP_ANIMATIONPARAMS);
            ap.style = BPAS_LINEAR;
            ap.dwDuration = m_uCurState != m_uNewState ? ANIMATION_DURATION : 0;
            CRect rcClient;
            GetClientRect(&rcClient);
            HDC hdcFrom = NULL, hdcTo = NULL;
            HANIMATIONBUFFER hbpAnimation = BeginBufferedAnimation(m_hWnd, dc, &rcClient, BPBF_COMPATIBLEBITMAP, NULL, &ap, &hdcFrom, &hdcTo);
            if( hbpAnimation != NULL ) {
               if( hdcFrom != NULL ) {
                  CDC dcCompat;
                  dcCompat.CreateCompatibleDC();
                  HBITMAP hOldBitmap = dcCompat.SelectBitmap(m_bmpCompat);
                  ::BitBlt(hdcFrom, 0, 0, rcClient.Width(), rcClient.Height(), dcCompat, 0, 0, SRCCOPY);
                  dcCompat.SelectBitmap(hOldBitmap);
               }
               if( hdcTo != NULL ) {
                  lRes = DefWindowProc(WM_PRINT, (WPARAM) hdcTo, PRF_ERASEBKGND | PRF_CLIENT | PRF_CHILDREN | PRF_CHECKVISIBLE);
                  if( ap.dwDuration > 0 ) {
                     // While the animation is going on, BufferedAnimation is fading between two images 
                     // on our client HDC. The actual child windows would block the painting effect, 
                     // so we need to turn them off. Unfortunately we cannot tell when the animation 
                     // has ended, so we use a timer to show the child windows again.
                     m_wndContents.ShowWindow(SW_HIDE);
                     m_wndList.ShowWindow(SW_HIDE);
                     SetTimer(FADESTOP_TIMERID, ANIMATION_DURATION + 50);
                  }
               }
               m_uCurState = m_uNewState;
               EndBufferedAnimation(hbpAnimation, TRUE);
            }
            else {
               lRes = DefWindowProc();
            }
         }
      }
      else
      {
         lRes = DefWindowProc();
      }

      return lRes;
   }

   LRESULT OnSize(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM lParam, BOOL& /*bHandled*/)
   {
      CRect rcClient(0, 0, LOWORD(lParam), HIWORD(lParam));
      _UpdateLayout(rcClient, 0);
      return 0;
   }

   // Implementation

   void _UpdateLayout(CRect rcClient, UINT uStyle)
   {
      if( !m_wndContents.IsWindow() ) return;
      const INT CX_TABLEOFCONTENTS = 120;
      if( m_bShowTOC ) {
         CRect rcContents(rcClient.left, rcClient.top, rcClient.left + CX_TABLEOFCONTENTS, rcClient.bottom);
         m_wndContents.SetWindowPos(NULL, rcContents, SWP_NOACTIVATE | SWP_NOZORDER | uStyle);
         rcClient.left += CX_TABLEOFCONTENTS;
      }
      else {
         m_wndContents.SetWindowPos(NULL, 0,0,0,0, SWP_HIDEWINDOW | (uStyle & SWP_NOREDRAW));
      }
      m_wndList.SetWindowPos(NULL, rcClient, SWP_NOACTIVATE | SWP_NOZORDER | uStyle);
   }

   bool _CaptureView()
   {
      // Now is a good time to capture the window because we're about to 
      // change view-state. Caller should Invalidate() the window and
      // set the new state so the BufferedAnimation can begin.
      if( !IsCompositionActive() ) return false;
      CRect rcClient;
      GetClientRect(&rcClient);
      CDC dcCompat;
      dcCompat.CreateCompatibleDC();
      if( !m_bmpCompat.IsNull() ) m_bmpCompat.DeleteObject();
      m_bmpCompat.CreateBitmap(rcClient.Width(), rcClient.Height(), 1, 32, NULL);
      HBITMAP hOldBitmap = dcCompat.SelectBitmap(m_bmpCompat);
      DefWindowProc(WM_PRINT, (WPARAM) dcCompat.m_hDC, PRF_ERASEBKGND | PRF_CLIENT | PRF_CHILDREN | PRF_CHECKVISIBLE);
      dcCompat.SelectBitmap(hOldBitmap);
      return true;
   }
};


#endif // !defined(AFX_LISTPANELVIEW_H__20071124_9610_2C38_D15D_0080AD509054__INCLUDED_)
